package source.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;

import source.events.action.MoveDirection;
import source.model.Position;
import source.model.ObjectID;
import source.model.type.ItemType;
import source.model.type.TerrainType;
import source.view.viewobjects.ViewItem;
import source.view.viewobjects.ViewObject;
import source.view.viewobjects.ViewTerrain;
import source.view.viewobjects.ViewUnit;
import source.model.type.GameObjectType;

public class ViewMap extends JComponent {
	public static final int TILE_WIDTH_PIXELS = 50;
	public static final int TILE_HEIGHT_PIXELS = 50;
	
	private int mapWidthTiles;
	private int mapHeightTiles;
	private AffineTransform gridTx;
	private ViewData viewData;
	private boolean[][] uncovered;

	
	public ViewMap(int mapWidth, int mapHeight, ViewData viewData){
		this.viewData = viewData;
		
		mapWidthTiles = mapWidth;
		mapHeightTiles = mapHeight;

		uncovered = new boolean[mapHeightTiles][mapWidthTiles];
		
		// Note that this mapWidthPixels is different than the overall width of the JComponent.
		int mapWidthPixels = mapWidth * TILE_WIDTH_PIXELS;
		int mapHeightPixels = mapHeight * TILE_WIDTH_PIXELS;
		
		double componentWidth = 2 * mapWidthPixels / 1.4;
		
		gridTx = new AffineTransform();
	
		// Assuming a square grid.  A rectangular grid would require something trickier.
		gridTx.translate(componentWidth/2, (componentWidth/2) * 7/10);
		gridTx.scale(1, .7);
		gridTx.rotate(-45 * Math.PI / 180);
		gridTx.translate(-mapWidthPixels/2, -mapHeightPixels/2);
		
		// Setting the dimesions for this component.
		this.setPreferredSize(new Dimension((int)componentWidth, (int)componentWidth * 7/10));
	}
	
	/**
	 * Map Width in number of Tiles
	 * @return
	 */
	public int getMapWidthTiles(){
		return this.mapWidthTiles;
	}
	
	/**
	 * Map Height in number of Tiles
	 * @return
	 */
	public int getMapHeightTiles(){
		return this.mapHeightTiles;
	}
	
	/**
	 * Map Width in pixels before being transformed.
	 * @return
	 */
	public int getMapWidthPixels(){
		return this.mapWidthTiles * TILE_WIDTH_PIXELS;
	}
	
	/**
	 * Map Height in pixels before being transformed.
	 * @return
	 */
	public int getMapHeightPixels(){
		return this.mapHeightTiles * TILE_HEIGHT_PIXELS;
	}
	

	
	/**
	 * transform the position of a tile to a point in the View Map coordinate system.
	 * @param pos
	 * @return
	 */
	public Point2D transform(Position pos){
		Point pt = new Point(pos.getX() * TILE_WIDTH_PIXELS, pos.getY() * TILE_HEIGHT_PIXELS);
		return gridTx.transform(pt, null);
	}
	
	/**
	 * returns the currently selected tile.
	 * @return
	 */
	public Position getSelectedTile(){
		return viewData.getSelectedTile();
	}
	
	public void paintComponent(Graphics g){
		Position selectedTile = viewData.getSelectedTile();
		List<Position> highlightedTiles = viewData.getHighlightedTiles();
		Hashtable<ViewTerrain, Position> terrainGrid = viewData.getTerrainGrid();
		Hashtable<ViewObject, Position> viewObjectGrid = viewData.getViewObjectGrid();
		
		Graphics2D g2 = (Graphics2D) g.create();

		//Draws the background
		BufferedImage img = ImageLibrary.getDefaultBackground();
		for(int row = 0; row * img.getWidth() <= this.getSize().width + 2 * img.getWidth(); row++){
			for(int col = 0; col * img.getHeight() <= this.getSize().height + 2 * img.getHeight(); col++){
				g2.drawImage(img, null, col * img.getWidth(), row * img.getHeight());
			}
		}
		g2.dispose();
		
		// calculate fog of war visibility
		updateFog();
		
		// fill in fog of war squares
		g2 = (Graphics2D) g.create();
		
		g2.transform(gridTx);
		for(int row = 0; row < uncovered.length; row++){
			for(int col = 0; col < uncovered[row].length; col++){
				if(!uncovered[row][col]){
					g2.setColor(new Color(10, 10, 10));
					g2.fillRect(row * TILE_HEIGHT_PIXELS, col * TILE_WIDTH_PIXELS, TILE_WIDTH_PIXELS, TILE_HEIGHT_PIXELS);
				}
			}
		}
		
		g2.dispose();

		// Draw the grid of the map.
		g2 = (Graphics2D) g.create();
		g2.transform(gridTx);
		
		g2.setColor(Color.DARK_GRAY);
		for(int i = 0; i <= mapWidthTiles; i++){
			g2.drawLine(i* TILE_HEIGHT_PIXELS, 0, i*TILE_HEIGHT_PIXELS, TILE_HEIGHT_PIXELS * mapHeightTiles);
			g2.drawLine(0, i*TILE_WIDTH_PIXELS, TILE_WIDTH_PIXELS * mapWidthTiles, i*TILE_WIDTH_PIXELS);
		}
		
		// Draw any selected or highlighted tiles.
		for(Position p: highlightedTiles){
			highlightTile(g2, Color.YELLOW, p);
		}
		if(selectedTile != null){
			highlightTile(g2, Color.BLUE, selectedTile);
		}
		g2.dispose();
		
		// Draw any terrains.
		Enumeration<ViewTerrain> terrains = terrainGrid.keys();
		while(terrains.hasMoreElements()){
			g2 = (Graphics2D) g.create();
			
			ViewTerrain vtObj = terrains.nextElement();
			Position p = terrainGrid.get(vtObj);
			
			if(uncovered[p.getX()][p.getY()])
			{
				Point pt = new Point(p.getX() * TILE_WIDTH_PIXELS, p.getY() * TILE_HEIGHT_PIXELS);
				gridTx.transform(pt, pt);
				
				g2.translate(pt.x + 10, pt.y - 25);
				vtObj.paint(g2);
			}
				
			g2.dispose();
		}
		
		// Draw all the Units in their relatively correct positions
		Enumeration<ViewObject> viewObjects = viewObjectGrid.keys();
		while(viewObjects.hasMoreElements()){
			g2 = (Graphics2D) g.create();
			
			ViewObject vobj = viewObjects.nextElement();
			Position p = viewObjectGrid.get(vobj);
			
			if(uncovered[p.getX()][p.getY()])
			{
				Point pt = new Point(p.getX() * TILE_WIDTH_PIXELS, p.getY() * TILE_HEIGHT_PIXELS);
				gridTx.transform(pt, pt);
				
				g2.translate(pt.x , pt.y);
				vobj.paint(g2);
			}
			
			g2.dispose();
		}
	}
	
	private void highlightTile(Graphics2D g, Color c, Position p){
		g.setColor(c);
		g.drawRect(p.getX() * TILE_WIDTH_PIXELS, p.getY() * TILE_HEIGHT_PIXELS, 
				TILE_WIDTH_PIXELS, TILE_HEIGHT_PIXELS);
		g.drawRect(p.getX() * TILE_WIDTH_PIXELS, p.getY() * TILE_HEIGHT_PIXELS, 
				TILE_WIDTH_PIXELS - 1, TILE_HEIGHT_PIXELS);
		g.drawRect(p.getX() * TILE_WIDTH_PIXELS, p.getY() * TILE_HEIGHT_PIXELS, 
				TILE_WIDTH_PIXELS - 1, TILE_HEIGHT_PIXELS-1);
	}
	
	void updateFog()
	{
		// update the fog of war
		
		java.util.Vector<source.model.Unit> playerUnits = viewData.getUnitsForPlayer();
		java.util.Vector<source.model.Base> playerBases = viewData.getStructuresForPlayer();
		
		// fill in the whole area
		for(int i = 0; i < mapHeightTiles; i++)
			for(int j = 0; j < mapWidthTiles; j++)
			{
				uncovered[i][j] = false;
			}
		
		// uncover bases
		for(source.model.Base current : playerBases)
		{
			Position unitPos = viewData.findViewObjectPosition(current.objectID());
			
			uncovered[unitPos.getX()][unitPos.getY()] = true;
		}
		
		// uncover units
		for(source.model.Unit current : playerUnits)
		{
			Position unitPos = viewData.findViewObjectPosition(current.objectID());
			
			uncovered[unitPos.getX()][unitPos.getY()] = true;
			
			// uncover area units can see
			for(int i = 0; i < mapHeightTiles; i++)
				for(int j = 0; j < mapWidthTiles; j++)
				{
					if(source.model.utility.PositionUtility.inRange(
							unitPos, 
							new Position(i, j), 
							current.getView())) uncovered[i][j] = true;
				}
		}
		
	}
	
/*	public static void main(String[] args){
		JFrame frame = new JFrame();
		
		ImageLibrary.init();
		
		ViewMap map = new ViewMap(10,10);
		
		Position.setBounds(10, 10);
		ViewUnit vob = new ViewUnit();
		map.add(vob, new Position(3,0));
		map.add(new ViewItem(ItemType.LASER), new Position(3,3));
		
		map.setSelectedTile(new Position(4,4));
		map.addHighlightedTile(new Position(5,4));
		map.addHighlightedTile(new Position(5,5));
		map.addTerrain(new ViewTerrain(TerrainType.PLANET), new Position(2,4));
		
		frame.getContentPane().add(new JScrollPane(map));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(400,400);
		
		frame.setVisible(true);
		
		try{
			map.uncoverTile(new Position(2,2));
			map.uncoverTile(2, 3);
			map.uncoverTile(2, 4);
			Thread.sleep(1000);
			map.move(vob, new Position(5,4));
			map.repaint();
			
			Thread.sleep(1000);
			map.move(vob, new Position(6,4));
			map.repaint();
			
			Thread.sleep(1000);
			//map.clearMap();
			map.clearHighlightedTiles();
			map.repaint();
		}
		catch(InterruptedException e){}
	}*/
}
